/*
 * Copyright (c) 2014, Mentor Graphics Corporation
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#ifndef ELF_LOADER_H_
#define ELF_LOADER_H_

#include <openamp/remoteproc.h>
#include <openamp/remoteproc_loader.h>

#if defined __cplusplus
extern "C" {
#endif

/* ELF32 base types - 32-bit. */
typedef uint32_t Elf32_Addr;
typedef uint16_t Elf32_Half;
typedef uint32_t Elf32_Off;
typedef int32_t  Elf32_Sword;
typedef uint32_t Elf32_Word;

/* ELF64 base types - 64-bit. */
typedef uint64_t Elf64_Addr;
typedef uint16_t Elf64_Half;
typedef uint64_t Elf64_Off;
typedef int32_t  Elf64_Sword;
typedef uint32_t Elf64_Word;
typedef uint64_t Elf64_Xword;
typedef int64_t  Elf64_Sxword;

/* Size of ELF identifier field in the ELF file header. */
#define EI_NIDENT 16

/* ELF32 file header (size: 52 bytes) */
typedef struct {
    unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
    Elf32_Half e_type;                /* Object file type */
    Elf32_Half e_machine;             /* Architecture */
    Elf32_Word e_version;             /* Object file version */
    Elf32_Addr e_entry;               /* Entry point virtual address */
    Elf32_Off e_phoff;                /* Program header table file offset */
    Elf32_Off e_shoff;                /* Section header table file offset */
    Elf32_Word e_flags;               /* Processor-specific flags */
    Elf32_Half e_ehsize;              /* ELF header size in bytes */
    Elf32_Half e_phentsize;           /* Program header table entry size */
    Elf32_Half e_phnum;               /* Program header table entry count */
    Elf32_Half e_shentsize;           /* Section header table entry size */
    Elf32_Half e_shnum;               /* Section header table entry count */
    Elf32_Half e_shstrndx;            /* Section header string table index */
} Elf32_Ehdr;

/* ELF64 file header (size: 64 bytes) */
typedef struct {
    unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
    Elf64_Half e_type;                /* Object file type */
    Elf64_Half e_machine;             /* Architecture */
    Elf64_Word e_version;             /* Object file version */
    Elf64_Addr e_entry;               /* Entry point virtual address */
    Elf64_Off e_phoff;                /* Program header table file offset */
    Elf64_Off e_shoff;                /* Section header table file offset */
    Elf64_Word e_flags;               /* Processor-specific flags */
    Elf64_Half e_ehsize;              /* ELF header size in bytes */
    Elf64_Half e_phentsize;           /* Program header table entry size */
    Elf64_Half e_phnum;               /* Program header table entry count */
    Elf64_Half e_shentsize;           /* Section header table entry size */
    Elf64_Half e_shnum;               /* Section header table entry count */
    Elf64_Half e_shstrndx;            /* Section header string table index */
} Elf64_Ehdr;

/* e_ident */
#define ET_NONE             0
#define ET_REL              1 /* Re-locatable file */
#define ET_EXEC             2 /* Executable file */
#define ET_DYN              3 /* Shared object file */
#define ET_CORE             4 /* Core file */
#define ET_LOOS             0xfe00 /* Operating system-specific */
#define ET_HIOS             0xfeff /* Operating system-specific */
#define ET_LOPROC           0xff00 /* remote_proc-specific */
#define ET_HIPROC           0xffff /* remote_proc-specific */

/* e_machine */
#define EM_ARM              40 /* ARM/Thumb Architecture */

/* e_version */
#define EV_CURRENT          1 /* Current version */

/* e_ident[] Identification Indexes */
#define EI_MAG0             0  /* File identification */
#define EI_MAG1             1  /* File identification */
#define EI_MAG2             2  /* File identification */
#define EI_MAG3             3  /* File identification */
#define EI_CLASS            4  /* File class */
#define EI_DATA             5  /* Data encoding */
#define EI_VERSION          6  /* File version */
#define EI_OSABI            7  /* Operating system/ABI identification */
#define EI_ABIVERSION       8  /* ABI version */
#define EI_PAD              9  /* Start of padding bytes */
#define EI_NIDENT           16 /* Size of e_ident[] */

/*
 * EI_MAG0 to EI_MAG3 - A file's first 4 bytes hold amagic number, identifying
 * the file as an ELF object file
 */
#define ELFMAG0             0x7f /* e_ident[EI_MAG0] */
#define ELFMAG1             'E'  /* e_ident[EI_MAG1] */
#define ELFMAG2             'L'  /* e_ident[EI_MAG2] */
#define ELFMAG3             'F'  /* e_ident[EI_MAG3] */
#define ELFMAG              "\177ELF"
#define SELFMAG             4

/*
 * EI_CLASS - The next byte, e_ident[EI_CLASS], identifies the file's class, or
 * capacity.
 */
#define ELFCLASSNONE        0 /* Invalid class */
#define ELFCLASS32          1 /* 32-bit objects */
#define ELFCLASS64          2 /* 64-bit objects */

/*
 * EI_DATA - Byte e_ident[EI_DATA] specifies the data encoding of the
 * remote_proc-specific data in the object file. The following encodings are
 * currently defined.
 */
#define ELFDATANONE         0 /* Invalid data encoding */
#define ELFDATA2LSB         1 /* See Data encodings, below */
#define ELFDATA2MSB         2 /* See Data encodings, below */

/* EI_OSABI - We do not define an OS specific ABI */
#define ELFOSABI_NONE       0

/* ELF32程序头 (大小: 32 字节) */
typedef struct elf32_phdr {
    Elf32_Word p_type;    /* segment type */
    Elf32_Off p_offset;   /* segment offset */
    Elf32_Addr p_vaddr;   /* virtual address of segment */
    Elf32_Addr p_paddr;   /* physical address of segment */
    Elf32_Word p_filesz;  /* number of bytes in file for seg */
    Elf32_Word p_memsz;   /* number of bytes in mem. for seg */
    Elf32_Word p_flags;   /* flags */
    Elf32_Word p_align;   /* memory alignment */
} Elf32_Phdr;

/* ELF64程序头 (大小: 56 字节) */
typedef struct elf64_phdr {
    Elf64_Word p_type;    /* segment type */
    Elf64_Word p_flags;   /* flags */
    Elf64_Off p_offset;   /* segment offset */
    Elf64_Addr p_vaddr;   /* virtual address of segment */
    Elf64_Addr p_paddr;   /* physical address of segment */
    Elf64_Xword p_filesz; /* number of bytes in file for seg */
    Elf64_Xword p_memsz;  /* number of bytes in mem. for seg */
    Elf64_Xword p_align;  /* memory alignment */
} Elf64_Phdr;

/* segment types */
#define PT_NULL             0
#define PT_LOAD             1
#define PT_DYNAMIC          2
#define PT_INTERP           3
#define PT_NOTE             4
#define PT_SHLIB            5
#define PT_PHDR             6
#define PT_TLS              7 /* Thread local storage segment */
#define PT_LOOS             0x60000000 /* OS-specific */
#define PT_HIOS             0x6fffffff /* OS-specific */
#define PT_LOPROC           0x70000000
#define PT_HIPROC           0x7fffffff

/* ELF32 section header (大小: 40 字节) */
typedef struct {
    Elf32_Word sh_name;       /* Section name (string tbl index) */
    Elf32_Word sh_type;       /* Section type */
    Elf32_Word sh_flags;      /* Section flags */
    Elf32_Addr sh_addr;       /* Section virtual addr at execution */
    Elf32_Off sh_offset;      /* Section file offset */
    Elf32_Word sh_size;       /* Section size in bytes */
    Elf32_Word sh_link;       /* Link to another section */
    Elf32_Word sh_info;       /* Additional section information */
    Elf32_Word sh_addralign;  /* Section alignment */
    Elf32_Word sh_entsize;    /* Entry size if section holds table */
} Elf32_Shdr;

/* ELF64 section header (大小: 64 字节) */
typedef struct {
    Elf64_Word sh_name;       /* Section name (string tbl index) */
    Elf64_Word sh_type;       /* Section type */
    Elf64_Xword sh_flags;     /* Section flags */
    Elf64_Addr sh_addr;       /* Section virtual addr at execution */
    Elf64_Off sh_offset;      /* Section file offset */
    Elf64_Xword sh_size;      /* Section size in bytes */
    Elf64_Word sh_link;       /* Link to another section */
    Elf64_Word sh_info;       /* Additional section information */
    Elf64_Xword sh_addralign; /* Section alignment */
    Elf64_Xword sh_entsize;   /* Entry size if section holds table */
} Elf64_Shdr;

/* sh_type */
#define SHT_NULL            0
#define SHT_PROGBITS        1
#define SHT_SYMTAB          2
#define SHT_STRTAB          3
#define SHT_RELA            4
#define SHT_HASH            5
#define SHT_DYNAMIC         6
#define SHT_NOTE            7
#define SHT_NOBITS          8
#define SHT_REL             9
#define SHT_SHLIB           10
#define SHT_DYNSYM          11
#define SHT_INIT_ARRAY      14
#define SHT_FINI_ARRAY      15
#define SHT_PREINIT_ARRAY   16
#define SHT_GROUP           17
#define SHT_SYMTAB_SHNDX    18
#define SHT_LOOS            0x60000000
#define SHT_HIOS            0x6fffffff
#define SHT_LOPROC          0x70000000
#define SHT_HIPROC          0x7fffffff
#define SHT_LOUSER          0x80000000
#define SHT_HIUSER          0xffffffff

/* sh_flags */
#define SHF_WRITE           0x1
#define SHF_ALLOC           0x2
#define SHF_EXECINSTR       0x4
#define SHF_MASKPROC        0xf0000000

/* Relocation entry (without addend) */
typedef struct {
    Elf32_Addr r_offset;
    Elf32_Word r_info;
} Elf32_Rel;

typedef struct {
    Elf64_Addr r_offset;
    Elf64_Xword r_info;
} Elf64_Rel;

/* Relocation entry with addend */
typedef struct {
    Elf32_Addr r_offset;
    Elf32_Word r_info;
    Elf32_Sword r_addend;
} Elf32_Rela;

typedef struct elf64_rela {
    Elf64_Addr r_offset;
    Elf64_Xword r_info;
    Elf64_Sxword r_addend;
} Elf64_Rela;

/* Macros to extract information from 'r_info' field of relocation entries */
#define ELF32_R_SYM(i)      ((i) >> 8)
#define ELF32_R_TYPE(i)     ((unsigned char)(i))
#define ELF64_R_SYM(i)      ((i) >> 32)
#define ELF64_R_TYPE(i)     ((i) & 0xffffffff)

/* Symbol table entry */
typedef struct {
    Elf32_Word st_name;
    Elf32_Addr st_value;
    Elf32_Word st_size;
    unsigned char st_info;
    unsigned char st_other;
    Elf32_Half st_shndx;
} Elf32_Sym;

typedef struct elf64_sym {
    Elf64_Word st_name;
    unsigned char st_info;
    unsigned char st_other;
    Elf64_Half st_shndx;
    Elf64_Addr st_value;
    Elf64_Xword st_size;
} Elf64_Sym;

/* ARM specific dynamic relocation codes */
#define R_ARM_GLOB_DAT      21 /* 0x15 */
#define R_ARM_JUMP_SLOT     22 /* 0x16 */
#define R_ARM_RELATIVE      23 /* 0x17 */
#define R_ARM_ABS32         2  /* 0x02 */

/* ELF32解码信息 (大小: 52 + 16 = 68 字节) */
struct elf32_info {
    Elf32_Ehdr ehdr;
    int load_state;
    Elf32_Phdr *phdrs;
    Elf32_Shdr *shdrs;
    void *shstrtab;
};

/* ELF64解码信息 (大小: 64 + 28 = 92 字节) */
struct elf64_info {
    Elf64_Ehdr ehdr;
    int load_state;
    Elf64_Phdr *phdrs;
    Elf64_Shdr *shdrs;
    void *shstrtab;
};

#define ELF_STATE_INIT              0x0L
#define ELF_STATE_WAIT_FOR_PHDRS    0x100L
#define ELF_STATE_WAIT_FOR_SHDRS    0x200L
#define ELF_STATE_WAIT_FOR_SHSTRTAB 0x400L
#define ELF_STATE_HDRS_COMPLETE     0x800L
#define ELF_STATE_MASK              0xFF00L
#define ELF_NEXT_SEGMENT_MASK       0x00FFL

extern const struct loader_ops elf_ops;

/**
 * @internal
 *
 * @brief Check if it is an ELF file
 *
 * It will check if the input image header is an ELF header.
 *
 * @param img_data Firmware private data which will be passed to user defined loader operations
 * @param len      Firmware header length
 *
 * @return 0 for success or negative value for failure.
 */
int elf_identify(const void *img_data, size_t len);

/**
 * @internal
 *
 * @brief Load ELF headers
 *
 * It will get the ELF header, the program header, and the section header.
 *
 * @param img_data        Image data
 * @param offset          Input image data offset to the start of image file
 * @param len             Input image data length
 * @param img_info        Pointer to store image information data
 * @param last_load_state Last state return by this function
 * @param noffset         Pointer to next offset required by loading ELF header
 * @param nlen            Pointer to next data length required by loading ELF header
 *
 * @return ELF loading header state, or negative value for failure
 */
int elf_load_header(const void *img_data, size_t offset, size_t len,
    void **img_info, int last_load_state, size_t *noffset, size_t *nlen);

/**
 * @internal
 *
 * @brief Load ELF data
 *
 * It will parse the ELF image and return the target device address,
 * offset to the start of the ELF image of the data to load and the
 * length of the data to load.
 *
 * @param rproc           Pointer to remoteproc instance
 * @param img_data        Image data which will passed to the function.
 *                        it can be NULL, if image data doesn't need to
 *                        be handled by the load function. E.g. binary
 *                        data which was loaded to the target memory.
 * @param offset          Last loaded image data offset to the start of image file
 * @param len             Last loaded image data length
 * @param img_info        Pointer to store image information data
 * @param last_load_state The returned state of the last function call.
 * @param da              目标设备地址, 如果要加载的数据不是目标内存的数据将被设置为ANY.
 * @param noffset         Pointer to next offset required by loading ELF header
 * @param nlen            Pointer to next data length required by loading ELF header
 * @param padding         Value to pad it is possible that a size of a segment
 *                        in memory is larger than what it is in the ELF image.
 *                        e.g. a segment can have stack section .bss.
 *                        It doesn't need to copy image file space,
 *                        in this case, it will be packed with 0.
 * @param nmemsize        Pointer to next data target memory size.
 *                        The size of a segment in the target memory can be
 *                        larger than the its size in the image file.
 *
 * @return 0 for success, otherwise negative value for failure
 */
int elf_load(struct remoteproc *rproc, const void *img_data, size_t offset,
    size_t len, void **img_info, int last_load_state, metal_phys_addr_t *da,
    size_t *noffset, size_t *nlen, unsigned char *padding, size_t *nmemsize);

/**
 * @internal
 *
 * @brief Release ELF image information
 *
 * It will release ELF image information data.
 *
 * @param img_info Pointer to ELF image information
 */
void elf_release(void *img_info);

/**
 * @internal
 *
 * @brief Get entry point
 *
 * It will return entry point specified in the ELF file.
 *
 * @param img_info	Pointer to ELF image information
 *
 * @return Entry address
 */
metal_phys_addr_t elf_get_entry(void *img_info);

/**
 * @internal
 *
 * @brief Locate the resource table information
 *
 * It will return the length of the resource table, and the device address of
 * the resource table.
 *
 * @param img_info Pointer to ELF image information
 * @param da       Pointer to the device address
 * @param offset   Pointer to the offset to in the ELF image of the resource table section.
 * @param size     Pointer to the size of the resource table section.
 *
 * @return 0 if successfully locate the resource table, negative value for failure.
 */
int elf_locate_rsc_table(void *img_info, metal_phys_addr_t *da, size_t *offset, size_t *size);

#if defined __cplusplus
}
#endif

#endif /* ELF_LOADER_H_ */
